Skip to content

Conversation

@BoxyUwU
Copy link
Member

@BoxyUwU BoxyUwU commented Oct 10, 2025

"remove normalize call"

Fixes #132765 in theory breaking as we might now treat some previously-ambig hr aliases as rigid when we shouldn't. we may want to explicitly normalize the coercion target in fcx.coerce like the new solver does.

"leak check and lub for closure<->closure coerce-lubs of same defids"

Fixes rust-lang/trait-system-refactor-initiative#233

fn peculiar() -> impl Fn(u8) -> u8 {
    return |x| x + 1
}

the |x| x + 1 expr has a type of Closure(?31t) which we wind up inferring the RPIT to. The CoerceMany ret_coercion for the whole peculiar typeck has an expected type of RPIT (unnormalized). When we type check the return |x| x + 1 expr we go from the never type to Closure(?31t) which then participates in the ret_coercion giving us a coerce-lub(RPIT, Closure(?31t)).

Normalizing RPIT gives us some Closure(?50t) where ?31t and ?50t have been unified with ?31t as the root var. resolve_vars_if_possible doesn't resolve infer vars to their roots so these wind up with different structural identities so the fast path doesn't apply and we fall back to coercing to a fn ptr. cc #147193 which also fixes this

New solver probably just gets more inference variables here because canonicalization + generally different approach to normalization of opaques. Idk :3

This technically allows more code to compile (see the test using TAIT in the commit). I don't think this can be observed on stable though.

In theory this could break existing code that relied on coercing a closure to itself resulting in a fnptr. I don't expect this to really happen in practice and this is an "obvious" bug fix.

leak check fndef<->fndef lubs of the same defids

when coerce-lub'ing a fndef with another fndef we already try to lub it first to avoid coercing to a fnptr unnecessarily. we now leak check after this lub operation.

this avoids us incorrectly considering the fndefs to be equal despite unequal binders and then winding up with borrow checker errors due when coercing to a fnptr would have worked fine. see tests/ui/coercion/leak_check_fndef_lub.rs

this is theoretically a breaking change as there could be dead code that relied on these unsatisfiable region constraints never being checked while also asserting that the output of the lub operation was an fndef. I don't think such code is likely to exist, and even if it does I am comfortable breaking it in favour of making code start working that expects us to coerce to a fnptr for unequal fndefs.

make coerce-lubs order independent

cc #73154 #97206

lub currently equates binders and then just returns the lhs without ever leak checking to ensure the binders are actually equal. this can lead to coerce-lub operations being order dependent, i.e. that a coerce-lub b has differing behaviour to b coerce-lub a.

we also cannot rely on borrow checking actually checking the constraints from equating the binders as lub is a hir-typeck coercion concept that is not relevant once MIR has been built. In the MIR we can simply subtype all the types of match arms with the final type of the match.

it is a forwards compatibility hazard to have coerce-lub be order dependent as it results in an effectively arbitrary inference choice which may or may not actually be correct or compatible with hypothetical future behaviours of lub that actually try to compute a proper lub'd type.

on stable we attempt to make coerce-lubs order independent with two checks:

  • Leak check for fndef->fnptr coercions
  • Leak check for fnptr->fnptr coercions

on stable we do this regardless of whether the coercion is occuring as part of a coerce-lub or not.

under this PR we have the following checks:

  • Leak check for fnptr/fndef->fnptr coercions
  • Leak check for closure->fnptr coercions performed as part of a coerce-lub
  • Leak check closure/fndef<->closure/fndef coerce-lubs that coerce both sides to fnptrs
  • Leak check the fallback lub operation when a coerce-lub does not actually coerce

this is theoretically breaking in two cases:

  1. the new leak checks are in dead code where borrowck doesn't emit any errors
  2. people have written matches/if-elses/array-exprs that rely on the arbitrary inference choice of the first signature encountered

the second case actually is encountered by a few crates in practice which has been somewhat mitigated by the next change.

improving binder lub

this PR also implements a slight improvement to lub operations on higher ranked types to avoid some regressions: #147565 (comment)

when lubbing two higher ranked types, if one of the binders is empty then we now instantiate the other binder with inference variables and lub the instantiated type with the empty-binder type.

concretely:

  • for<'a> fn(&'a ()) lub fn(&'static ())
  • only the lhs has bound vars so: fn(&'?a ()) lub fn(&'static ())
  • '?a eq 'static passes leak check

whereas the previous behaviour was:

  • for<'a> fn(&'a ()) lub fn(&'static ())
  • lubbing binders is hard so just equate with invariance: fn(&'!a ()) eq fn(&'static ())
  • '!a eq 'static fails leak check

"account for safe target features in fndef<->closure and fndef<->fndef coerce-lubs"

We previously did not take safe target features into account when creating the fn sig for fndefs during fndef<->closure or fndef<->fndef coerce-lubs. We now do allowing coerce-lub to produce a safe fn pointer when fndefs with safe target features are involved. This is consistent with existing (non lub) coercion logic for fndef->fnptr which allows coercing to a safe fn pointer.

r? ghost

@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Oct 10, 2025
@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

@BoxyUwU BoxyUwU force-pushed the coercion_cleanup branch 2 times, most recently from bce0933 to eb6434f Compare October 29, 2025 18:00
@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

@BoxyUwU BoxyUwU force-pushed the coercion_cleanup branch 2 times, most recently from 797201d to 3ecd1a8 Compare October 30, 2025 14:42
@rust-log-analyzer

This comment has been minimized.

@BoxyUwU BoxyUwU force-pushed the coercion_cleanup branch 2 times, most recently from 6179239 to 2958ef0 Compare October 30, 2025 15:19
rust-bors bot added a commit that referenced this pull request Nov 4, 2025
@rustbot rustbot added the S-waiting-on-perf Status: Waiting on a perf run to be completed. label Nov 4, 2025
@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

@rust-bors
Copy link

rust-bors bot commented Nov 4, 2025

☀️ Try build successful (CI)
Build commit: e0046e9 (e0046e9cb815c2977039794d2ab0e6badfd9581b, parent: e5efc336720901420a8891dcdb67ca0a475dc03c)

@rust-timer

This comment has been minimized.

@rust-timer
Copy link
Collaborator

Finished benchmarking commit (e0046e9): comparison URL.

Overall result: ❌ regressions - please read the text below

Benchmarking this pull request means it may be perf-sensitive – we'll automatically label it not fit for rolling up. You can override this, but we strongly advise not to, due to possible changes in compiler perf.

Next Steps: If you can justify the regressions found in this try perf run, please do so in sufficient writing along with @rustbot label: +perf-regression-triaged. If not, please fix the regressions and do another perf run. If its results are neutral or positive, the label will be automatically removed.

@bors rollup=never
@rustbot label: -S-waiting-on-perf +perf-regression

Instruction count

Our most reliable metric. Used to determine the overall result above. However, even this metric can be noisy.

mean range count
Regressions ❌
(primary)
0.2% [0.2%, 0.2%] 2
Regressions ❌
(secondary)
0.3% [0.2%, 0.6%] 25
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
- - 0
All ❌✅ (primary) 0.2% [0.2%, 0.2%] 2

Max RSS (memory usage)

Results (secondary 1.2%)

A less reliable metric. May be of interest, but not used to determine the overall result above.

mean range count
Regressions ❌
(primary)
- - 0
Regressions ❌
(secondary)
1.2% [1.2%, 1.2%] 1
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
- - 0
All ❌✅ (primary) - - 0

Cycles

Results (primary -2.6%)

A less reliable metric. May be of interest, but not used to determine the overall result above.

mean range count
Regressions ❌
(primary)
- - 0
Regressions ❌
(secondary)
- - 0
Improvements ✅
(primary)
-2.6% [-2.6%, -2.6%] 1
Improvements ✅
(secondary)
- - 0
All ❌✅ (primary) -2.6% [-2.6%, -2.6%] 1

Binary size

This benchmark run did not return any relevant results for this metric.

Bootstrap: 473.413s -> 474.101s (0.15%)
Artifact size: 390.72 MiB -> 390.76 MiB (0.01%)

@rustbot rustbot removed the S-waiting-on-perf Status: Waiting on a perf run to be completed. label Nov 5, 2025
@craterbot
Copy link
Collaborator

🚧 Experiment pr-147565-1 is now running

ℹ️ Crater is a tool to run experiments across parts of the Rust ecosystem. Learn more

@BoxyUwU BoxyUwU force-pushed the coercion_cleanup branch 2 times, most recently from be7daf8 to 73228a9 Compare November 5, 2025 15:27
@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

We do this by:
1. Leak check for closure->fnptr coercions performed as part of a coerce-lub
2. Leak check closure<->fndef, fndef<->fndef and closure<->closure coercions to fnptrs
3. Leak check the fallback `lub` operation when a coerce-lub does not actually coerce

note that fndef->fnptr and fnptr->fnptr coercions performed as part of a
coerce-lub were already leak checked on stable and we continue to do so.
- leak checking the lub for fndef<->fndef coerce-lubs
- start lubbing closure<->closure coerce-lubs and leak check it
@craterbot
Copy link
Collaborator

🎉 Experiment pr-147565-1 is completed!
📊 6 regressed and 0 fixed (1696 total)
📊 95 spurious results on the retry-regessed-list.txt, consider a retry1 if this is a significant amount.
📰 Open the summary report.

⚠️ If you notice any spurious failure please add them to the denylist!
ℹ️ Crater is a tool to run experiments across parts of the Rust ecosystem. Learn more

Footnotes

  1. re-run the experiment with crates=https://crater-reports.s3.amazonaws.com/pr-147565-1/retry-regressed-list.txt

@craterbot craterbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-crater Status: Waiting on a crater run to be completed. labels Nov 5, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

I-lang-radar Items that are on lang's radar and will need eventual work or consideration. perf-regression Performance regression. S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-clippy Relevant to the Clippy team. T-types Relevant to the types team, which will review and decide on the PR/issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

FulfillmentErrorCode::Project ICE for opaques [ICE]: index out of bounds

6 participants